﻿/*
    This file is part of News Worm.

    News Worm is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    News Worm is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with News Worm.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.IO;
using System.Collections.Generic;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Syndication;
using System.Xml;
using System.Text;

namespace NewsWorm {
    public class Feed : IFeed{
        internal Feed( string url, int max, ScriptingEvents events) {
            RefreshOrCreate(null, url, max, events); 
        }

        //This is only called when loading from xml on startup.
        internal Feed( string url, string title, string desc) {
            this.m_Url = url;
            this.m_Title = title;
            this.m_Description = desc;
            this.m_IsCustomSearchFeed = false;
            m_Items = new List<FeedItem>();
            
            FeedAdjuster adj = this.GetFeedAdjusterForStdFeedByUrl( url );
            if ( adj != null ) {
                this.OverrideCount = adj.ArticleCountWanted;
            }
        }

        internal Feed( CombinedSearchFeed CombinedFeed ) {
            FromCombinedFeed( CombinedFeed );
        }

        private void FromCombinedFeed( CombinedSearchFeed CombinedFeed ) {
            m_IsCustomSearchFeed = true;
            m_CombinedSearchFeed = CombinedFeed;

            m_Title = CombinedFeed.Title;
            m_Description = CombinedFeed.Description;
            m_Items = CombinedFeed.Items;

            //Set parent feed to this. loop all items and set parent again.
            foreach ( FeedItem item in m_Items )
                item.SetParentFeed( this );

            //Guid will works a key.
            //If there are several custom feeds with same title, url is used to find and remove the feed
            m_Url = CombinedFeed.UrlGuid;

            FeedAdjuster adj = this.GetFeedAdjusterForStdFeedByUrl( m_Url );
            if ( adj != null ) {
                this.OverrideCount = adj.ArticleCountWanted;
            }
        }

        private string m_Url;
        private string m_Title;
        private string m_Description;
        private List<FeedItem> m_Items;
        private int m_OverrideCount;
        private bool m_HasOverride = false;
        private bool m_Deleted = false;
        private bool m_IsCustomSearchFeed = false;
        private CombinedSearchFeed m_CombinedSearchFeed;
        private bool m_HasNotification = true;
        private bool m_WebServerRunning = false;
        private WebServer m_Server;
        private string m_HostedRssLink = "";

        internal bool HasNotification {
            get { return m_HasNotification; }
            set { m_HasNotification = value; }
        }

        internal string RSSFeedXml( HttpListenerRequest request ) {
            return new RSS2Creator( this,m_HostedRssLink ).ToString();
        }

        public string StartWebServer() {
            if ( !m_WebServerRunning ) {
                string guid = Guid.NewGuid().ToString();
                string url = "http://localhost:8080/" + guid + "/";
                m_HostedRssLink = url;
                m_Server = new WebServer( RSSFeedXml, url );
                m_Server.Start();
                m_WebServerRunning = true;
                return url;
            }

            return null;
        }

        public void StopWebServer() {
            if ( m_WebServerRunning ) {
                m_Server.Stop();
                m_WebServerRunning = false;
            }
        }

        internal CombinedSearchFeed CombinedSearchFeed {
            get { return m_CombinedSearchFeed; }
        }

        internal bool IsCustomSearchFeed {
            get { return m_IsCustomSearchFeed; }
            set { m_IsCustomSearchFeed = value; }
        }

        public ICustomFeed CustomFeed {
            get {return  m_CombinedSearchFeed; }
        }

        public bool IsCustomFeed {
            get { return IsCustomSearchFeed; }
        }

        internal void AddItem( FeedItem item ) {
            if ( !this.m_Items.Contains( item ) )
                this.m_Items.Add( item );
        }

        internal int GetUnreadArticleCount( Dictionary<string, string> Visited ) {
            int count = 0;

            foreach ( FeedItem item in m_Items ) {
                foreach ( KeyValuePair<string, string> kvp in Visited ) {
                    if ( kvp.Key.Equals( item.Url ) )
                        count++;
                }
            }

            return this.m_Items.Count - count;
        }

        private FeedAdjuster GetFeedAdjusterForStdFeedByUrl( string url ) {
            string file = Variables.BasePath + "override.dat";
            FeedAdjuster adjuster = null;

            if ( File.Exists( file ) ) {
                StreamReader reader = new StreamReader( Variables.BasePath + "override.dat" );
                string line;

                while ( ( line = reader.ReadLine() ) != null ) {
                    string[] sep = new string[] { "||||" };
                    string[] str = line.Split( sep, StringSplitOptions.None );
                    //str[0] url, str[1] override count
                    if ( str[0].Equals( url ) ) {
                        adjuster = new FeedAdjuster( Int32.Parse( str[1] ) );
                        this.m_HasOverride = true;
                        this.m_OverrideCount = adjuster.ArticleCountWanted;
                    }
                }

                reader.Close();
            }

            return adjuster;
        }

        public bool WebServerRunning {
            get { return m_WebServerRunning; }
        }

        public bool DoNotLoad {
            get { return m_Deleted; }
            set { m_Deleted = value; }
        }

        public bool HasNotificationEnabled { get { return this.HasNotification; } }

        private SyndicationFeed GetSyndicationFeed(string url) {
            SyndicationFeed f = null;
            WebRequest req = WebRequest.Create( url );
            
            req.Timeout = 5000;
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.DtdProcessing = DtdProcessing.Ignore;
            using ( WebResponse rep = req.GetResponse() ) {
                using ( XmlReader r = XmlReader.Create( rep.GetResponseStream(), settings ) ) {
                    f = SyndicationFeed.Load( r );
                }
            }

            return f;
        }

        //This function is only called when m_IsCustomSearchFeed = true
        private void RefreshCombinedFeed( List<string> urls, int max, ScriptingEvents events) {
            FeedAdjuster adj = this.GetFeedAdjusterForStdFeedByUrl( this.Url );
            if ( adj != null ) {
                max = adj.ArticleCountWanted;
            }
            m_CombinedSearchFeed.CombineAndRefresh(urls, max, events);
            m_Items = m_CombinedSearchFeed.Items;

            foreach ( FeedItem item in m_Items )
                item.SetParentFeed( this );
        }

      
        private string GetOriginalFeedRssSourceXml( SyndicationFeed f ) {
            string retval;
            StringBuilder b = new StringBuilder();
           
            XmlWriter wr = XmlWriter.Create(b );
            f.SaveAsRss20( wr );
            wr.Close();

            retval = b.ToString();
            return retval;
            
        }

        internal void RefreshOrCreate( List<string> urls, string url, int max, ScriptingEvents events) {
            try {
                if ( !m_IsCustomSearchFeed ) {

                    int count = 0;
                    m_Items = new List<FeedItem>();
                    SyndicationFeed f = GetSyndicationFeed( url );
                   
                    if ( f == null )
                        throw new Exception( "Failed to fetch feed from " + url );

                    FeedAdjuster adj = this.GetFeedAdjusterForStdFeedByUrl( url );

                    if ( adj != null ) {
                        max = adj.ArticleCountWanted;
                    }

                    foreach ( SyndicationItem item in f.Items ) {
                        //Some broken feeds might include empty items....
                        //check that there is no dublicate feeditems

                        if ( item.Title.Text != "" ) {
                            FeedItem feedItem = new FeedItem( item, this );
                            if ( !m_Items.Contains( feedItem ) ) {
                                if ( max == 0 ) {
                                    m_Items.Add( feedItem );
                                } else {
                                    if ( count < max ) {
                                        m_Items.Add( feedItem );
                                        count++;
                                    }
                                }
                            }
                        }
                    }

                    m_Title = f.Title.Text;
                    m_Description = f.Description.Text;
                    m_Url = url;
                    
                    events.RaiseOnRefreshFeed( this );

                } else {
                    RefreshCombinedFeed(urls, max, events);
                }

            } catch ( Exception e ) {
                Console.Write( e.Message );
            }
        }

        internal bool HasOverrideCount {
            get { return m_HasOverride; }
            set {
                m_HasOverride = value;
            }
        }

        internal int OverrideCount {
            get { return m_OverrideCount; }
            set {
                m_OverrideCount = value;
            }
        }

        public string Url { get { return m_Url; } }
        public string Title { get { return m_Title; } }
        public string Description { get { return m_Description; } }
        internal List<FeedItem> Items { get { return m_Items; } }

        public override bool Equals( object obj ) {
            Feed f = (Feed)obj;
            if ( f != null ) {
                if ( f.Url != null ) {
                    if ( f.Url.Equals( this.Url ) )
                        return true;
                }
            }
            return false;
        }

        public override int GetHashCode() {
            return base.GetHashCode();
        }
    }
}
